﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Thrift.Transport;

namespace Thrift.Utility
{
    internal class ThriftFactory
    {
        private static volatile List<ServiceTransportPool> transport_pools;
        
        private static object sync_obj = new object();

        private static IThriftFactoryMonitor monitor = new ThriftFactoryMonitor();

        static ThriftFactory()
        {
            if (transport_pools == null || transport_pools.Count == 0)
            {
                lock (sync_obj)
                {
                    if (transport_pools == null || transport_pools.Count == 0)
                    {
                        var services = ConfigHelper.GetServiceConfigs();
                        transport_pools = new List<ServiceTransportPool>(services.Count);
                        foreach (var service in services)
                        {
                            ServiceTransportPool stp = new ServiceTransportPool()
                            {
                                ServiceConfig = service,
                                TransportPool = new ConcurrentStack<TTransport>(),
                                ResetEvent = new AutoResetEvent(false),
                                ActivedTransportCount = 0
                            };
                            transport_pools.Add(stp);
                        }
                    }
                }
            }
            if(!string.IsNullOrWhiteSpace(ConfigHelper.ThriftConfig.MonitorType))
            {
                monitor = Invoker.CreateInstance(Type.GetType(ConfigHelper.ThriftConfig.MonitorType)) as IThriftFactoryMonitor;
                if (monitor == null)
                {
                    throw new ThriftException(string.Format("There Is No Monitor Implement Which Type Is  \"{0}\"", ConfigHelper.ThriftConfig.MonitorType));
                }
            }
        }

        public static TTransport BorrowInstance(string serviceName)
        {
            var transpool = (from tp in transport_pools where tp.ServiceConfig.Name.ToUpper() == serviceName.ToUpper() select tp).FirstOrDefault();
            if (transpool == null)
            {
                throw new ThriftException(string.Format("There Is No Service Named \"{0}\"", serviceName));
            }

            TTransport transport;

            lock (transpool.sync_obeject)
            {
                if (transpool.TransportPool.Count() == 0)
                {
                    if (transpool.ActivedTransportCount == transpool.ServiceConfig.MaxActive)
                    {
                        bool result = transpool.ResetEvent.WaitOne(transpool.ServiceConfig.WaitingTimeout);
                        if (!result)
                        {
                            monitor.TimeoutNotify(transpool.ServiceConfig.Name, transpool.ServiceConfig.WaitingTimeout);
                        }
                    }
                    else
                    {
                        transpool.TransportPool.Push(CreateTransport(transpool.ServiceConfig));
                    }
                }

                if (!transpool.TransportPool.TryPop(out transport))
                {
                    throw new ThriftException("Connection Pool Exception");
                }

                transpool.ActivedTransportCount++;

                if (transpool.TransportPool.Count() < transpool.ServiceConfig.MinIdle && transpool.ActivedTransportCount < transpool.ServiceConfig.MaxActive)
                {
                    transpool.TransportPool.Push(CreateTransport(transpool.ServiceConfig));
                }
            }
            if (!transport.IsOpen)
            {
                transport.Open();
            }
            Monitor();
            return transport;
        }
        
        public static void ReturnInstance(string serviceName,TTransport transport)
        {
            var transpool = (from tp in transport_pools where tp.ServiceConfig.Name.ToUpper() == serviceName.ToUpper() select tp).FirstOrDefault();
            if (transpool == null)
            {
                throw new ThriftException("Connection Pool Exception");
            }
            if (transpool.TransportPool.Count() == transpool.ServiceConfig.MaxIdle)
            {
                transport.Flush();
                if (transport.IsOpen)
                {
                    transport.Close();
                }
                transport.Dispose();
            }
            else
            {
                lock (transpool.sync_obeject)
                {
                    if (transport.IsOpen)
                    {
                        transport.Close();
                    }
                    transpool.TransportPool.Push(transport);
                    transpool.ActivedTransportCount--;
                    transpool.ResetEvent.Set();
                }
            }
            Monitor();
        }

        private static TTransport CreateTransport(ServiceConfig config)
        {
            TTransport transport = new TSocket(config.IP, config.Port);
            transport.Open();
            return transport;
        }

        /// <summary>
        /// 监控连接池状态
        /// </summary>
        private static void Monitor()
        {
            List<Tuple<string, int, int>> tuples = new List<Tuple<string, int, int>>(transport_pools.Count);
            foreach(var transpool in transport_pools)
            {
                Tuple<string, int, int> tuple = new Tuple<string, int, int>(transpool.ServiceConfig.Name, transpool.TransportPool.Count(), transpool.ActivedTransportCount);
                tuples.Add(tuple);
            }
            monitor.Monitor(tuples);
        }
    }

    public interface IThriftFactoryMonitor
    {
        /// <summary>
        /// 监控连接池运行状态
        /// </summary>
        /// <param name="tuple">元组集合，第一个元素表示服务名称、第二个元素表示空闲连接数量、第三个元素表示激活连接数量</param>
        void Monitor(List<Tuple<string,int,int>> tuples);

        /// <summary>
        /// 等待连接超时
        /// </summary>
        void TimeoutNotify(string serviceName,int timeOut);
    }

    /// <summary>
    /// 默认连接池状态监控类
    /// </summary>
    public class ThriftFactoryMonitor : IThriftFactoryMonitor
    {
        public virtual void Monitor(List<Tuple<string, int, int>> tuples)
        {
            foreach (var t in tuples)
            {
                Console.WriteLine(string.Format("{0}连接池，空闲连接数量：{1}，激活连接数量：{2}", t.Item1, t.Item2, t.Item3));
            }
        }

        public virtual void TimeoutNotify(string serviceName, int timeOut)
        {
            Console.WriteLine(string.Format("{0}连接池等待连接超时{1}", serviceName, timeOut));
        }
    }

    internal class ServiceTransportPool
    {
        public ServiceConfig ServiceConfig { get; set; }

        public ConcurrentStack<TTransport> TransportPool { get; set; }

        public AutoResetEvent ResetEvent { get; set; }

        public int ActivedTransportCount { get; set; }

        internal object sync_obeject = new object();
    }
}
